package cc.rengu.igas.share.core.service.channel.esb;

import cc.rengu.igas.channel.esb.facade.base.Header;
import cc.rengu.igas.channel.esb.facade.bean.ChnlMchntInfo;
import cc.rengu.igas.channel.esb.facade.bean.ChnlPayeeInfo;
import cc.rengu.igas.channel.esb.facade.bean.ChnlPayerInfo;
import cc.rengu.igas.channel.esb.facade.bean.ChnlTransInfo;
import cc.rengu.igas.channel.esb.facade.request.D0RealTimeEntryToBankAcctRequest;
import cc.rengu.igas.channel.esb.facade.response.D0RealTimeEntryToBankAcctResponse;
import cc.rengu.igas.channel.esb.facade.result.Result;
import cc.rengu.igas.share.common.constant.ShareParamConstant;
import cc.rengu.igas.share.common.constant.ShareServiceConstant;
import cc.rengu.igas.share.common.constant.TransCacheConstant;
import cc.rengu.oltp.service.base.ChannelBaseService;
import cc.rengu.oltp.service.common.constant.AppParamConstant;
import cc.rengu.oltp.service.common.constant.TreeNodeConstant;
import cc.rengu.oltp.service.common.entity.ChannelCallCfg;
import cc.rengu.oltp.service.common.entity.MchntOrderInfo;
import cc.rengu.oltp.service.common.entity.PayOrderInfo;
import cc.rengu.oltp.service.common.enums.*;
import cc.rengu.oltp.service.model.BizException;
import cc.rengu.oltp.service.model.BizResponse;
import cc.rengu.oltp.service.model.OrderInfo;
import cc.rengu.oltp.service.model.TxnInfo;
import cc.rengu.oltp.service.realize.DubboService;
import cc.rengu.oltp.service.realize.impl.DubboServiceImpl;
import cc.rengu.oltp.utility.util.*;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

/**
 * 商户实时入账服务
 * Created by wangcheng on 2021/9/3 17:22
 */
public class MchntD0RealTimeAcctService extends ChannelBaseService {
    @Override
    public Object callPreProc(TxnInfo txnInfo, ChannelCallCfg channelCallCfg, OrderInfo orderInfo) throws Exception {
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        String sysSeqNum = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.SYS_SEQ_NUM);
        String transDate = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.TRANS_DATE);
        String transTime = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.TRANS_TIME);
        //获取需要实时入账的商户订单信息
        MchntOrderInfo mchntOrderInfo;
        if (null != orderInfo.getMchntOrderInfoList() && orderInfo.getMchntOrderInfoList().size() == 1) {
            mchntOrderInfo = orderInfo.getMchntOrderInfoList().get(0);
        } else {
            String realAcctMchntNo = (String) GlobalTransUtil.getCacheValue(TransCacheConstant.REAL_ACCT_MCHNT_NO);
            Optional<MchntOrderInfo> mchntOrderInfoOpt = orderInfo.getMchntOrderInfoList().stream().filter(item -> item.getMchntNo().equals(realAcctMchntNo)).findFirst();
            if (StringUtil.isEmptyOrNull(realAcctMchntNo) || !mchntOrderInfoOpt.isPresent()) {
                rglog.error("当前商户订单中不存在要进行实时入账的商户订单信息!");
                return null;
            }
            mchntOrderInfo = mchntOrderInfoOpt.get();
        }
        //通道请求报文头赋值
        Header esbHeader = new Header();
        esbHeader.setInstId(xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.INST_ID));
        esbHeader.setChanlId(channelCallCfg.getMsgSrcId());
        esbHeader.setSrcSysId(AppParamConstant.SYS_ID);
        esbHeader.setBizType(AppParamConstant.DEFAULT_BIZ_TYPE);
        esbHeader.setVersion(ShareParamConstant.DUBBO_VSERSION);
        esbHeader.setTxnNum(channelCallCfg.getCallChannelTxn());
        esbHeader.setTraceNo(sysSeqNum + mchntOrderInfo.getMchntNo());
        esbHeader.setTransDate(transDate);
        esbHeader.setTransTime(transTime);
        //通道请求报文体赋值
        D0RealTimeEntryToBankAcctRequest d0RealTimeEntryToBankAcctRequest = new D0RealTimeEntryToBankAcctRequest();
        d0RealTimeEntryToBankAcctRequest.setHeader(esbHeader);
        //商户相关信息赋值
        ChnlMchntInfo chnlMchntInfo = new ChnlMchntInfo();
        BeanUtil.beanCopy(mchntOrderInfo, chnlMchntInfo);
        d0RealTimeEntryToBankAcctRequest.setChnlMchntInfo(chnlMchntInfo);
        //付款人相关信息赋值
        ChnlPayerInfo chnlPayerInfo = new ChnlPayerInfo();
        chnlPayerInfo.setPayerAcctInfo(xmlTreeUtil.getXmlTreeStringValue("acctNo"));
        d0RealTimeEntryToBankAcctRequest.setChnlPayerInfo(chnlPayerInfo);
        //收款人相关信息赋值
        ChnlPayeeInfo chnlPayeeInfo = new ChnlPayeeInfo();
        d0RealTimeEntryToBankAcctRequest.setChnlPayeeInfo(chnlPayeeInfo);
        //交易相关信息赋值
        ChnlTransInfo chnlTransInfo = new ChnlTransInfo();
        BeanUtil.beanCopy(orderInfo.getUserOrderInfo(), chnlTransInfo);
        chnlTransInfo.setOrderId(orderInfo.getUserOrderInfo().getUserOrderId());
        chnlTransInfo.setTransCcy(orderInfo.getUserOrderInfo().getOrderCcy());
        chnlTransInfo.setTransAmt(orderInfo.getUserOrderInfo().getOrderAmt());
        d0RealTimeEntryToBankAcctRequest.setChnlTransInfo(chnlTransInfo);
        return d0RealTimeEntryToBankAcctRequest;
    }

    @Override
    public PayOrderInfo generatePayOrderInfo(ChannelCallCfg channelCallCfg, OrderInfo orderInfo, Object request) throws Exception {
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        PayOrderInfo payOrderInfo = new PayOrderInfo();
        D0RealTimeEntryToBankAcctRequest d0RealTimeEntryToBankAcctRequest = (D0RealTimeEntryToBankAcctRequest) request;
        BeanUtil.beanCopy(orderInfo.getUserOrderInfo(), payOrderInfo);
        if (orderInfo.getMchntOrderInfoList().size() == 1) {
            payOrderInfo.setMchntNo(orderInfo.getMchntOrderInfoList().get(0).getMchntNo());
        } else {
            payOrderInfo.setMchntNo(orderInfo.getUserOrderInfo().getGroupMchntNo());
        }
        payOrderInfo.setTxnNum(channelCallCfg.getTxnNum());
        payOrderInfo.setBizType(channelCallCfg.getBizType());
        payOrderInfo.setSysSeqNum(orderInfo.getUserOrderInfo().getSysSeqNum());
        payOrderInfo.setOrderCcy(orderInfo.getUserOrderInfo().getOrderCcy());
        payOrderInfo.setMsgDstId(channelCallCfg.getCallChannelId());
        payOrderInfo.setDstChannelType("D0");
        payOrderInfo.setDstCallIndex(channelCallCfg.getCallIndex());
        payOrderInfo.setChannelSeqNum(Objects.requireNonNull(d0RealTimeEntryToBankAcctRequest).getHeader().getTraceNo());
        payOrderInfo.setChannelReconKey(d0RealTimeEntryToBankAcctRequest.getHeader().getTraceNo());
        payOrderInfo.setDstTxnNum(channelCallCfg.getCallChannelTxn());
        payOrderInfo.setPayType(PayTypeEnum.BANK_CARD.getPayType());
        payOrderInfo.setPayerAcctType(AcctTypeEnum.BANK_DEBIT.getAcctType());
        payOrderInfo.setPayerAcctInfo(d0RealTimeEntryToBankAcctRequest.getChnlPayerInfo().getPayerAcctInfo());
        payOrderInfo.setBizStatus(BizStatusEnum.PROCESSING.getStatus());
        payOrderInfo.setTransStatus(TransStatusEnum.TRANS_INIT.getStatus());
        payOrderInfo.setCreateTime(DateUtil.getCurrentDateTime("yyyy-MM-dd HH:mm:ss.SSS"));
        payOrderInfo.setUserId(orderInfo.getUserOrderInfo().getUserId());
        return payOrderInfo;
    }

    @Override
    public Object callProc(ChannelCallCfg channelCallCfg, Object request) throws Exception {
        D0RealTimeEntryToBankAcctRequest d0RealTimeEntryToBankAcctRequest = (D0RealTimeEntryToBankAcctRequest) request;
        /* Step1：调用接口，获取结果 */
        Result<D0RealTimeEntryToBankAcctResponse> callResult;
        DubboService dubboService = new DubboServiceImpl();
        if (AppParamConstant.YES.equals(channelCallCfg.getSyncCallFlag())) {
            /* Step1.1：异步调用 */
            int iReturnCode;
            int callTimes = 0 == channelCallCfg.getSyncQueryRepeatNum() ? 1 : channelCallCfg.getSyncQueryRepeatNum();
            int gatTime = channelCallCfg.getSyncQueryRepeatTime();
            int gatType = StringUtil.isEmptyOrNull(channelCallCfg.getSyncQueryGapType()) ? 0 : Integer.parseInt(channelCallCfg.getSyncQueryGapType());
            if (ChannelCallModeEnum.LOCAL.getMode().equals(channelCallCfg.getChannelCallMode())) {
                /* Step1.1.1：异步应用内调用 */
                Class clasz = Class.forName(ShareServiceConstant.BANK_CORE_SERVICE_INNER_PATH);
                iReturnCode = dubboService.asynCallDubboInnerMethod(clasz, ShareServiceConstant.D0_REAL_TIME_ENTRY_TO_BANK_ACCT_METHOD, d0RealTimeEntryToBankAcctRequest, channelCallCfg.getSyncResultServiceName(), callTimes, gatType, gatTime);
            } else if (ChannelCallModeEnum.REMOTE.getMode().equals(channelCallCfg.getChannelCallMode())) {
                /* Step1.1.2：异步远程调用 */
                iReturnCode = dubboService.asynCallDubboService(ShareServiceConstant.BANK_CORE_SERVICE, ShareServiceConstant.D0_REAL_TIME_ENTRY_TO_BANK_ACCT_METHOD, d0RealTimeEntryToBankAcctRequest, channelCallCfg.getSyncResultServiceName(), callTimes, gatType, gatTime);
            } else {
                rglog.error("不支持的通道服务调用方式:{}，交易拒绝!", channelCallCfg.getChannelCallMode());
                throw new BizException(OltpRpcdEnum.CALL_BIZ_SERVICE_ERROR);
            }
            if (0 == iReturnCode) {
                return new BizResponse<>(OltpRpcdEnum.TRANS_SUCCESS.getRespCode(), OltpRpcdEnum.TRANS_SUCCESS.getRespDesc());
            } else {
                return new BizResponse<>(OltpRpcdEnum.CALL_BIZ_SERVICE_ERROR.getRespCode(), OltpRpcdEnum.CALL_BIZ_SERVICE_ERROR.getRespDesc());
            }
        } else {
            /* Step1.2：同步调用 */
            if (ChannelCallModeEnum.LOCAL.getMode().equals(channelCallCfg.getChannelCallMode())) {
                /* Step1.2.1：同步应用内调用 */
                Class clasz = Class.forName(ShareServiceConstant.BANK_CORE_SERVICE_INNER_PATH);
                callResult = (Result<D0RealTimeEntryToBankAcctResponse>) dubboService.callDubboInnerMethod(clasz, ShareServiceConstant.D0_REAL_TIME_ENTRY_TO_BANK_ACCT_METHOD, d0RealTimeEntryToBankAcctRequest);
            } else if (ChannelCallModeEnum.REMOTE.getMode().equals(channelCallCfg.getChannelCallMode())) {
                /* Step1.2.2：同步远程调用 */
                callResult = (Result<D0RealTimeEntryToBankAcctResponse>) dubboService.callDubboService(ShareServiceConstant.BANK_CORE_SERVICE, ShareServiceConstant.D0_REAL_TIME_ENTRY_TO_BANK_ACCT_METHOD, d0RealTimeEntryToBankAcctRequest);
            } else {
                rglog.error("不支持的通道服务调用方式:{}，交易拒绝!", channelCallCfg.getChannelCallMode());
                throw new BizException(OltpRpcdEnum.CALL_BIZ_SERVICE_ERROR);
            }
        }
        return callResult;
    }

    @Override
    public Object querySyncCallResult(PayOrderInfo payOrderInfo, Object request) {
        return null;
    }

    @Override
    public OrderInfo callResultOrderProc(OrderInfo orderInfo, String payOrderIndex, TransStatusEnum transStatusEnum, Object response) throws Exception {
        Result<D0RealTimeEntryToBankAcctResponse> callResult = (Result<D0RealTimeEntryToBankAcctResponse>) response;
        OrderInfo updateOrderInfo = new OrderInfo();
        Optional<PayOrderInfo> payOrderInfoOpt = orderInfo.getPayOrderInfoList().stream().filter(item -> item.getDstCallIndex().equals(payOrderIndex)).findFirst();
        if (payOrderInfoOpt.isPresent() && null != callResult && null != callResult.getResult()) {
            PayOrderInfo payOrderInfo = payOrderInfoOpt.get();
            D0RealTimeEntryToBankAcctResponse d0RealTimeEntryToBankAcctResponse = callResult.getResult();
            payOrderInfo.setTransStatus(transStatusEnum.getStatus());
            payOrderInfo.setRespCode(d0RealTimeEntryToBankAcctResponse.getRespCode());
            payOrderInfo.setRespDesc(d0RealTimeEntryToBankAcctResponse.getRespDesc());
            payOrderInfo.setUpdateTime(DateUtil.getCurrentDateTime("yyyy-MM-dd HH:mm:ss.SSS"));
            switch (transStatusEnum) {
                case TRANS_SUCC:
                    payOrderInfo.setBizStatus(BizStatusEnum.FINISH.getStatus());
                    updateOrderInfo.setUserOrderInfo(orderInfo.getUserOrderInfo());
                    payOrderInfo.setSettleDate(DateUtil.getCurrentDate());
                    break;
                case TRANS_TIMEOUT:
                    payOrderInfo.setBizStatus(BizStatusEnum.PROCESSING.getStatus());
                    break;
                case SYSTEM_REJECT:
                    payOrderInfo.setBizStatus(BizStatusEnum.FAILED.getStatus());
                    break;
                default:
                    break;
            }
            List<PayOrderInfo> payOrderInfoList = new ArrayList<>();
            payOrderInfoList.add(payOrderInfo);
            updateOrderInfo.setPayOrderInfoList(payOrderInfoList);
        }
        return updateOrderInfo;
    }
}
